#include "EffectTemplatePCH.h"
#include "Application.h"
#include "ElapsedTime.h"
#include "EffectManager.h"
#include "Effect.h"
#include "EffectParameter.h"
#include "Technique.h"
#include "Pass.h"

#include "Events.h"

static const GLubyte
myDemonTextureImage[3*(128*128)] = {
    /* RGB8 image data for a mipmapped 128x128 demon texture */
#include "demon_image.h"
};
GLuint g_DemonTextureID = 0;

Application g_App( "Effect Template", 512, 512 );

void OnKeyPressed( KeyEventArgs& e );
void OnMouseButtonPressed( MouseButtonEventArgs& e );
void OnMouseMoved( MouseMotionEventArgs& e );
void OnResized( ResizeEventArgs& e );
void OnUpdate( UpdateEventArgs& e );
void OnRender( RenderEventArgs& e );

void OnInitialize( EventArgs& e );
void OnEffectLoaded( EffectLoadedEventArgs& e );
void OnTerminate( EventArgs& e );

static const int gs_iNumScenes = 3;
static int gs_iCurrentScene = 0;

void RenderScene0();
void RenderScene1();

int main( int argc, char* argv[] )
{

    // Register events
    g_App.KeyPressed += OnKeyPressed;
    g_App.MouseButtonPressed += OnMouseButtonPressed;
    g_App.MouseMoved += OnMouseMoved;
    g_App.Resize += OnResized;
    
    g_App.Render += OnRender;
    g_App.Update += OnUpdate;
    g_App.Initialized += OnInitialize;
    g_App.Terminated += OnTerminate;

    return g_App.Run();
}

void OnInitialize( EventArgs& e )
{
    // Create an effect manager
    EffectManager::Create().Initialize();
    EffectManager& effectMgr = EffectManager::Get();

    effectMgr.EffectLoaded += OnEffectLoaded;

    // First load the resources that are used by the effects before the effects are loaded.
    glGenTextures( 1, &g_DemonTextureID );
    glBindTexture(GL_TEXTURE_2D, g_DemonTextureID);

    /* Load demon decal texture with mipmaps. */
    gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, 128, 128, GL_RGB, GL_UNSIGNED_BYTE, myDemonTextureImage );

    // Load the default effect
    effectMgr.CreateEffectFromFile( "Shaders/C3E1_anycolor.cgfx", "C3E1_anycolor" );
    effectMgr.CreateEffectFromFile( "Shaders/C3E3_texture.cgfx", "C3E3_texture");    
}

void OnEffectLoaded( EffectLoadedEventArgs& e )
{
    // Set the properties for the effects
    Effect& effect = e.Effect;
    if ( effect.GetEffectName() == "C3E3_texture" )
    {
        EffectParameter& textureParameter = effect.GetParameterByName("decalSampler");
        textureParameter.Set( g_DemonTextureID );
        textureParameter.UpdateParameter();
    }
}

void OnTerminate( EventArgs& e )
{
    // Destroy the effect manager
    EffectManager::Destroy();

    // Delete any resources used by the application.
    if ( g_DemonTextureID != 0 )
    {
        glDeleteTextures( 1, &g_DemonTextureID );
        g_DemonTextureID = 0;
    }
}

void OnKeyPressed( KeyEventArgs& e )
{
    switch ( e.Key )
    {
    case ( SDLK_ESCAPE ):
        {
            g_App.Stop();
        }
        break;
    case ( SDLK_1 ):
        {
            gs_iCurrentScene = 0;
        }
        break;  
    case ( SDLK_2 ):
        {
            gs_iCurrentScene = 1;
        }
        break;
    }
}

void OnMouseButtonPressed( MouseButtonEventArgs& e )
{

}

void OnMouseMoved( MouseMotionEventArgs& e )
{

}

void OnResized( ResizeEventArgs& e )
{
    std::cout << "Window Resized: " << e.Dimensions.x << ", " << e.Dimensions.y << std::endl;
}

void OnUpdate( UpdateEventArgs& e )
{
    static float fReloadTimer = 0.0f;

    // Every seconds, we'll check to see if the effects need to be reloaded.
    fReloadTimer += e.ElapsedTime;
    if ( fReloadTimer > 2.0f )
    {
        EffectManager::Get().ReloadEffects();
        fReloadTimer = 0.0f;
    }
}

void OnRender( RenderEventArgs& e )
{
    glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );

    switch ( gs_iCurrentScene )
    {
    case 0:
        {
            RenderScene0();
        }
        break;
    case 1:
        {
            RenderScene1();
        }
        break;
    }

    g_App.Present();
}

void RenderScene0()
{
    static ElapsedTime elapsedTime;
    static float fTotalTime = 0;
    float fElapsedTime = elapsedTime.GetElapsedTime();

    fTotalTime += fElapsedTime;

    Effect& greenEffect = EffectManager::Get().GetEffect( "C3E1_anycolor" );
    EffectParameter& constantColor = greenEffect.GetParameterByName("gConstantColor");
    if ( constantColor.IsValid() )
    {
        float red = ( sinf( fTotalTime ) + 1.0f ) * 0.5f;
        float green = ( cosf( fTotalTime ) + 1.0f ) * 0.5f;

        glm::vec4 color( red, green, 0.0f, 1.0f );
        constantColor.Set( color );
        constantColor.UpdateParameter();
    }

    Technique& technique = greenEffect.GetFirstValidTechnique();

    technique.BeginTechnique();
    foreach( Pass* pass, technique.GetPasses() )
    {        
        if ( pass->BeginPass() )
        {
            /* Rendering code verbatim from Chapter 1, Section 2.4.1 "Rendering
            a Triangle with OpenGL" (page 57). */
            glBegin(GL_TRIANGLES);
            {
                glVertex2f(-0.8f, 0.8f);
                glVertex2f(0.8f, 0.8f);
                glVertex2f(0.0f, -0.8f);
            }
            glEnd();

            pass->EndPass();
        }
    }
}

void RenderScene1()
{
    Effect& textureEffect = EffectManager::Get().GetEffect( "C3E3_texture" );
    Technique& technique = textureEffect.GetFirstValidTechnique();

    technique.BeginTechnique();
    foreach( Pass* pass, technique.GetPasses() )
    {        
        if ( pass->BeginPass() )
        {
            glBegin(GL_TRIANGLES);
            {
                glTexCoord2f(0.0f, 0.0f);
                glVertex2f(-0.8f, 0.8f);

                glTexCoord2f(1.0f, 0.0f);
                glVertex2f(0.8f, 0.8f);

                glTexCoord2f(0.5f, 1.0f);
                glVertex2f(0.0f, -0.8f);
            }
            glEnd();

            pass->EndPass();
        }
    }
}